package LUDOSimulator;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import GA.GAPlayer;

public class LUDO_new extends Frame implements ActionListener
{
	private FileWriter fstream_wins, fstream_weights, fstream_all_wins;
	private BufferedWriter out_wins, out_weights, out_all_wins;
	
	private static final long serialVersionUID = 1L;
	public static boolean visual = false;
	private ArrayList<LUDOPlayer> players = new ArrayList<LUDOPlayer>();
	private ArrayList<LUDOPlayer> evaluationPlayersSemi = new ArrayList<LUDOPlayer>();
	
	static LUDOBoard board;
	public LUDO_new() 
	{
		super("LUDO Simulator");
		
		try
		{
			fstream_wins = new FileWriter("out_wins.txt");
			fstream_weights = new FileWriter("out_weights.txt");
			fstream_all_wins = new FileWriter("out_all_wins.txt");
		}
		catch (IOException e)
		{ System.out.println("Her1"); }
		
		out_wins = new BufferedWriter(fstream_wins);
		out_weights = new BufferedWriter(fstream_weights);
		out_all_wins = new BufferedWriter(fstream_all_wins);
		
	    setBackground(Color.white);
		board = new LUDOBoard();
		add(board, BorderLayout.CENTER);
    
		Menu optionsMenu = new Menu("Options", true);  
		optionsMenu.add("Reset Game");
		optionsMenu.addActionListener(this);

		MenuBar mbar = new MenuBar();  
	    mbar.add(optionsMenu);
    
		setMenuBar(mbar); // Add the menu bar to the frame.
		setBounds(30,50,1000,800);  // Set size and position of window.
    
		setResizable(false);
		addWindowListener(
	       new WindowAdapter() {
	            public void windowClosing(WindowEvent evt) {
	               LUDO_new.this.dispose();
				   System.exit(0);
				   
	            }
	         }
         );
		setVisible(visual);
		
		//Create list of semi smart players used to evaluate the each generation
		evaluationPlayersSemi.add(new SemiSmartLUDOPlayer(board));
		evaluationPlayersSemi.add(new SemiSmartLUDOPlayer(board));
		evaluationPlayersSemi.add(new SemiSmartLUDOPlayer(board));
		
		//1. Create population of candidate solutions
		for (int i = 0; i < 100; i++)
		{
			int noOfWeights = 10;
			int[] weights = new int[noOfWeights];
			for (int j = 0; j < noOfWeights; j++)
			{
				int weight;
				do
				{
					weight = (int)(Math.random() * 100 + 1);
				} while (!Helper.isUnique(weights, weight));
				weights[j] = weight;
			}

			LUDOPlayer player = new DbjLUDOPlayer(board, weights);
			players.add(player);
		}
		
		//2. Evaluate population.
		getBestPlayer(Tournament.playEvaluationRound(players, evaluationPlayersSemi));
	}
	
	public LUDO_new(int j) 
	{
		super("LUDO Simulator");
		
	    setBackground(Color.white);
		board = new LUDOBoard();
		add(board, BorderLayout.CENTER);
    
		Menu optionsMenu = new Menu("Options", true);  
		optionsMenu.add("Reset Game");
		optionsMenu.addActionListener(this);

		MenuBar mbar = new MenuBar();  
	    mbar.add(optionsMenu);
    
		setMenuBar(mbar); // Add the menu bar to the frame.
		setBounds(30,50,1000,800);  // Set size and position of window.
    
		setResizable(false);
		addWindowListener(
	       new WindowAdapter() {
	            public void windowClosing(WindowEvent evt) {
	               LUDO_new.this.dispose();
				   System.exit(0);
				   
	            }
	         }
         );
		setVisible(visual);

		ArrayList<LUDOPlayer> sbt = new ArrayList<LUDOPlayer>();
		sbt.add(new GAPlayer(board));
		sbt.add(new GAPlayer(board));
		sbt.add(new GAPlayer(board));
		ArrayList<LUDOPlayer> dbj = new ArrayList<LUDOPlayer>();
		dbj.add(new DbjLUDOPlayer(board));
		dbj.add(new DbjLUDOPlayer(board));
		dbj.add(new DbjLUDOPlayer(board));
		ArrayList<LUDOPlayer> ishan = new ArrayList<LUDOPlayer>();
		ishan.add(new IshanLUDOPlayer(board));
		ishan.add(new IshanLUDOPlayer(board));
		ishan.add(new IshanLUDOPlayer(board));
		ArrayList<LUDOPlayer> random = new ArrayList<LUDOPlayer>();
		random.add(new RandomLUDOPlayer(board));
		random.add(new RandomLUDOPlayer(board));
		random.add(new RandomLUDOPlayer(board));
		
		LUDOPlayer sbt_single = new GAPlayer(board);
		LUDOPlayer dbj_single = new DbjLUDOPlayer(board);		
		LUDOPlayer semi_single = new SemiSmartLUDOPlayer(board);
		
		//Evaluate dbj
		try
		{
			fstream_wins = new FileWriter("dbj_wins.txt");
		
			out_wins = new BufferedWriter(fstream_wins);

			PlayerResult dbjYellowSbt = Tournament.playFinalRound(dbj_single, sbt, LUDOBoard.YELLOW);
			PlayerResult dbjBlueSbt = Tournament.playFinalRound(dbj_single, sbt, LUDOBoard.BLUE);
			PlayerResult dbjGreenSbt = Tournament.playFinalRound(dbj_single, sbt, LUDOBoard.GREEN);
			PlayerResult dbjRedSbt = Tournament.playFinalRound(dbj_single, sbt, LUDOBoard.RED);
			out_wins.write(dbjYellowSbt.getGames() + "\t" + dbjYellowSbt.getWins() + "\t" + dbjYellowSbt.getResult());
			out_wins.flush();
			out_wins.write(dbjBlueSbt.getGames() + "\t" + dbjBlueSbt.getWins() + "\t" + dbjBlueSbt.getResult());
			out_wins.flush();
			out_wins.write(dbjGreenSbt.getGames() + "\t" + dbjGreenSbt.getWins() + "\t" + dbjGreenSbt.getResult());
			out_wins.flush();
			out_wins.write(dbjRedSbt.getGames() + "\t" + dbjRedSbt.getWins() + "\t" + dbjRedSbt.getResult());
			out_wins.flush();

			PlayerResult dbjYellowIshan = Tournament.playFinalRound(dbj_single, ishan, LUDOBoard.YELLOW);
			PlayerResult dbjBlueIshan = Tournament.playFinalRound(dbj_single, ishan, LUDOBoard.BLUE);
			PlayerResult dbjGreenIshan = Tournament.playFinalRound(dbj_single, ishan, LUDOBoard.GREEN);
			PlayerResult dbjRedIshan = Tournament.playFinalRound(dbj_single, ishan, LUDOBoard.RED);
			out_wins.write(dbjYellowIshan.getGames() + "\t" + dbjYellowIshan.getWins() + "\t" + dbjYellowIshan.getResult());
			out_wins.flush();
			out_wins.write(dbjBlueIshan.getGames() + "\t" + dbjBlueIshan.getWins() + "\t" + dbjBlueIshan.getResult());
			out_wins.flush();
			out_wins.write(dbjGreenIshan.getGames() + "\t" + dbjGreenIshan.getWins() + "\t" + dbjGreenIshan.getResult());
			out_wins.flush();
			out_wins.write(dbjRedIshan.getGames() + "\t" + dbjRedIshan.getWins() + "\t" + dbjRedIshan.getResult());
			out_wins.flush();

			PlayerResult dbjYellowRandom = Tournament.playFinalRound(dbj_single, random, LUDOBoard.YELLOW);
			PlayerResult dbjBlueRandom = Tournament.playFinalRound(dbj_single, random, LUDOBoard.BLUE);
			PlayerResult dbjGreenRandom = Tournament.playFinalRound(dbj_single, random, LUDOBoard.GREEN);
			PlayerResult dbjRedRandom = Tournament.playFinalRound(dbj_single, random, LUDOBoard.RED);
			out_wins.write(dbjYellowRandom.getGames() + "\t" + dbjYellowRandom.getWins() + "\t" + dbjYellowRandom.getResult());
			out_wins.flush();
			out_wins.write(dbjBlueRandom.getGames() + "\t" + dbjBlueRandom.getWins() + "\t" + dbjBlueRandom.getResult());
			out_wins.flush();
			out_wins.write(dbjGreenRandom.getGames() + "\t" + dbjGreenRandom.getWins() + "\t" + dbjGreenRandom.getResult());
			out_wins.flush();
			out_wins.write(dbjRedRandom.getGames() + "\t" + dbjRedRandom.getWins() + "\t" + dbjRedRandom.getResult());
			out_wins.flush();
			
			out_wins.close();
			fstream_wins.close();
		}
		catch (IOException e)
		{ e.printStackTrace(); }
		
		//Evaluate sbt
		try
		{
			fstream_wins = new FileWriter("sbt_wins.txt");
		
			out_wins = new BufferedWriter(fstream_wins);
			
			PlayerResult sbtYellow = Tournament.playFinalRound(sbt_single, dbj, LUDOBoard.YELLOW);
			PlayerResult sbtBlue = Tournament.playFinalRound(sbt_single, dbj, LUDOBoard.BLUE);
			PlayerResult sbtGreen = Tournament.playFinalRound(sbt_single, dbj, LUDOBoard.GREEN);
			PlayerResult sbtRed = Tournament.playFinalRound(sbt_single, dbj, LUDOBoard.RED);
			out_wins.write(sbtYellow.getGames() + "\t" + sbtYellow.getWins() + "\t" + sbtYellow.getResult());
			out_wins.flush();
			out_wins.write(sbtBlue.getGames() + "\t" + sbtBlue.getWins() + "\t" + sbtBlue.getResult());
			out_wins.flush();
			out_wins.write(sbtGreen.getGames() + "\t" + sbtGreen.getWins() + "\t" + sbtGreen.getResult());
			out_wins.flush();
			out_wins.write(sbtRed.getGames() + "\t" + sbtRed.getWins() + "\t" + sbtRed.getResult());
			out_wins.flush();

			PlayerResult sbtYellowIshan = Tournament.playFinalRound(sbt_single, ishan, LUDOBoard.YELLOW);
			PlayerResult sbtBlueIshan = Tournament.playFinalRound(sbt_single, ishan, LUDOBoard.BLUE);
			PlayerResult sbtGreenIshan = Tournament.playFinalRound(sbt_single, ishan, LUDOBoard.GREEN);
			PlayerResult sbtRedIshan = Tournament.playFinalRound(sbt_single, ishan, LUDOBoard.RED);
			out_wins.write(sbtYellowIshan.getGames() + "\t" + sbtYellowIshan.getWins() + "\t" + sbtYellowIshan.getResult());
			out_wins.flush();
			out_wins.write(sbtBlueIshan.getGames() + "\t" + sbtBlueIshan.getWins() + "\t" + sbtBlueIshan.getResult());
			out_wins.flush();
			out_wins.write(sbtGreenIshan.getGames() + "\t" + sbtGreenIshan.getWins() + "\t" + sbtGreenIshan.getResult());
			out_wins.flush();
			out_wins.write(sbtRedIshan.getGames() + "\t" + sbtRedIshan.getWins() + "\t" + sbtRedIshan.getResult());
			out_wins.flush();

			PlayerResult sbtYellowRandom = Tournament.playFinalRound(sbt_single, ishan, LUDOBoard.YELLOW);
			PlayerResult sbtBlueRandom = Tournament.playFinalRound(sbt_single, ishan, LUDOBoard.BLUE);
			PlayerResult sbtGreenRandom = Tournament.playFinalRound(sbt_single, ishan, LUDOBoard.GREEN);
			PlayerResult sbtRedRandom = Tournament.playFinalRound(sbt_single, ishan, LUDOBoard.RED);
			out_wins.write(sbtYellowRandom.getGames() + "\t" + sbtYellowRandom.getWins() + "\t" + sbtYellowRandom.getResult());
			out_wins.flush();
			out_wins.write(sbtBlueRandom.getGames() + "\t" + sbtBlueRandom.getWins() + "\t" + sbtBlueRandom.getResult());
			out_wins.flush();
			out_wins.write(sbtGreenRandom.getGames() + "\t" + sbtGreenRandom.getWins() + "\t" + sbtGreenRandom.getResult());
			out_wins.flush();
			out_wins.write(sbtRedRandom.getGames() + "\t" + sbtRedRandom.getWins() + "\t" + sbtRedRandom.getResult());
			out_wins.flush();

			out_wins.close();
			fstream_wins.close();
		}
		catch (IOException e)
		{ e.printStackTrace(); }
		
		//Evaluate semi vs. sbt
		try
		{
			fstream_wins = new FileWriter("semi_sbt_wins.txt");
		
			out_wins = new BufferedWriter(fstream_wins);
			
			PlayerResult smYellow = Tournament.playFinalRound(semi_single, sbt, LUDOBoard.YELLOW);
			PlayerResult smBlue = Tournament.playFinalRound(semi_single, sbt, LUDOBoard.BLUE);
			PlayerResult smGreen = Tournament.playFinalRound(semi_single, sbt, LUDOBoard.GREEN);
			PlayerResult smRed = Tournament.playFinalRound(semi_single, sbt, LUDOBoard.RED);
			out_wins.write(smYellow.getGames() + "\t" + smYellow.getWins() + "\t" + smYellow.getResult());
			out_wins.flush();
			out_wins.write(smBlue.getGames() + "\t" + smBlue.getWins() + "\t" + smBlue.getResult());
			out_wins.flush();
			out_wins.write(smGreen.getGames() + "\t" + smGreen.getWins() + "\t" + smGreen.getResult());
			out_wins.flush();
			out_wins.write(smRed.getGames() + "\t" + smRed.getWins() + "\t" + smRed.getResult());
			out_wins.flush();

			out_wins.close();
			fstream_wins.close();
		}
		catch (IOException e)
		{ e.printStackTrace(); }

		//Evaluate semi vs. dbj
		try
		{
			fstream_wins = new FileWriter("semi_dbj_wins.txt");
		
			out_wins = new BufferedWriter(fstream_wins);
			
			PlayerResult smYellow = Tournament.playFinalRound(semi_single, dbj, LUDOBoard.YELLOW);
			PlayerResult smBlue = Tournament.playFinalRound(semi_single, dbj, LUDOBoard.BLUE);
			PlayerResult smGreen = Tournament.playFinalRound(semi_single, dbj, LUDOBoard.GREEN);
			PlayerResult smRed = Tournament.playFinalRound(semi_single, dbj, LUDOBoard.RED);
			out_wins.write(smYellow.getGames() + "\t" + smYellow.getWins() + "\t" + smYellow.getResult());
			out_wins.flush();
			out_wins.write(smBlue.getGames() + "\t" + smBlue.getWins() + "\t" + smBlue.getResult());
			out_wins.flush();
			out_wins.write(smGreen.getGames() + "\t" + smGreen.getWins() + "\t" + smGreen.getResult());
			out_wins.flush();
			out_wins.write(smRed.getGames() + "\t" + smRed.getWins() + "\t" + smRed.getResult());
			out_wins.flush();

			out_wins.close();
			fstream_wins.close();
		}
		catch (IOException e)
		{ e.printStackTrace(); }
	}
	
	//At the end of the execution of this method, the best solution is found
/*	private void getBestPlayer(List<PlayerResult> result)
	{		
		List<PlayerResult> parentSelection;
		List<LUDOPlayer> newPopulation;
		List<PlayerResult> evaluatedPopulation = result;
		
		int generations = 100;

		for (int i = 0; i < generations; i++)
		{
			parentSelection = Helper.findBestPlayers(evaluatedPopulation);			//Select parent(s). Based on a rank-based selection. Best 20 % are returned
			newPopulation = Helper.createPopulation(parentSelection);				//Apply operator(s)
			evaluatedPopulation = Tournament.playRound(newPopulation);				//Evaluate the new population
			
			PlayerResult theBest = Helper.findBestPlayers(evaluatedPopulation).get(0);
			PlayerResult the2ndBest = Helper.findBestPlayers(evaluatedPopulation).get(1);
			PlayerResult the3rdBest = Helper.findBestPlayers(evaluatedPopulation).get(2);
			System.out.println("Games played: " + theBest.getGames() + ", won: " + theBest.getWins());
			System.out.println("Games played: " + the2ndBest.getGames() + ", won: " + the2ndBest.getWins());
			System.out.println("Games played: " + the3rdBest.getGames() + ", won: " + the3rdBest.getWins());

			//Evaluation of the three best players in each generation
			PlayerResult no1best = Tournament.evaluateBestPlayer(theBest.getPlayer(), evaluationPlayers);
			System.out.println("Evaluated - Games played " + no1best.getGames() + ", won: " + no1best.getWins());
			PlayerResult no2best = Tournament.evaluateBestPlayer(the2ndBest.getPlayer(), evaluationPlayers);
			System.out.println("Evaluated - Games played " + no2best.getGames() + ", won: " + no2best.getWins());
			PlayerResult no3best = Tournament.evaluateBestPlayer(the3rdBest.getPlayer(), evaluationPlayers);
			System.out.println("Evaluated - Games played " + no3best.getGames() + ", won: " + no3best.getWins());
		}
		
		PlayerResult bestResult = Helper.findBestPlayers(evaluatedPopulation).get(0);
		LUDOPlayer theBest = bestResult.getPlayer();
		
		System.out.println("Games played: " + bestResult.getGames() + ", won: " + bestResult.getWins());
	}*/
	
	private void getBestPlayer(List<PlayerResult> result)
	{		
		List<PlayerResult> parentSelection;
		List<LUDOPlayer> newPopulation;
		List<PlayerResult> evaluatedPopulation = result;
		
		printResult(evaluatedPopulation, 0);
		
		int generations = 100;

		for (int i = 1; i < generations; i++)
		{
			parentSelection = Helper.findBestPlayers(Helper.sortPlayers(evaluatedPopulation));			//Select parent(s). Based on a rank-based selection. Best 20 % are returned
			newPopulation = Helper.createPopulation(parentSelection);				//Apply operator(s)
			
			evaluatedPopulation = Tournament.playEvaluationRound(newPopulation, evaluationPlayersSemi);		//Evaluate the new population
			
			printResult(evaluatedPopulation, i);
		}
		
		try
		{
			out_wins.close();
			out_weights.close();
			out_all_wins.close();
		} catch (IOException e)
		{ e.printStackTrace(); }
	}
	
	private void printResult(List<PlayerResult> evaluatedPopulation, int generationI)
	{
		List<PlayerResult> sortedList = Helper.sortPlayers(evaluatedPopulation);
		PlayerResult theBest = sortedList.get(0);
		PlayerResult the2ndBest = sortedList.get(1);
		PlayerResult the3rdBest = sortedList.get(2);
		System.out.println("\nGeneration: " + generationI);
		System.out.println("Games played: " + theBest.getGames() + ", won: " + theBest.getWins() + ", points: " + theBest.getResult());
		System.out.println("Games played: " + the2ndBest.getGames() + ", won: " + the2ndBest.getWins() + ", points: " + the2ndBest.getResult());
		System.out.println("Games played: " + the3rdBest.getGames() + ", won: " + the3rdBest.getWins() + ", points: " + the3rdBest.getResult());
		
		DbjLUDOPlayer winner = (DbjLUDOPlayer) theBest.getPlayer();

		try
		{
			int totalWins = 0, totalPoints = 0, totalGames = 0;
			for (PlayerResult pr : sortedList)
			{
				totalWins = totalWins + pr.getWins();
				totalPoints = totalPoints + pr.getResult();
				totalGames = totalGames + pr.getGames();
				out_all_wins.write(pr.getWins() + ", ");
			}
			out_all_wins.newLine();
			out_all_wins.flush();
			int avgWinsPrc = totalWins * 100 / totalGames;
			int avgWins = totalWins /sortedList.size();
			int avgPoints = totalPoints /sortedList.size();
			out_wins.write(avgWinsPrc + "\t" + avgWins + "\t" + avgPoints + "\t" + theBest.getGames() + "," + theBest.getWins() + "," + the2ndBest.getWins() + "," + the3rdBest.getWins() + "," + theBest.getResult() + "," + the2ndBest.getResult() + "," + the3rdBest.getResult());
			out_wins.newLine();
			
			for (int weight : winner.getWeights())
			{
				out_weights.write(weight + ",");
			}
			out_weights.newLine();
			out_wins.flush();
			out_weights.flush();
		}
		catch (IOException e)
		{ System.out.println("Her2"); }
	}

	public void actionPerformed(ActionEvent event) 
	{
		if(event.getActionCommand()=="Reset Game")
			board.kill();
	}
	
	public static void main(String[] args)
	{
		new LUDO_new(1);
	}
}